package com.cobalt4nt.photomap;

import android.location.Address;
import android.location.Geocoder;
import android.media.ExifInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
//import android.support.v4.app.DialogFragment;
//import android.support.v4.app.FragmentActivity;

import android.util.Log;
import android.view.View;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.SortedSet;
import java.util.TreeSet;

import static android.os.Environment.getExternalStorageDirectory;
import static com.google.android.gms.common.GooglePlayServicesUtil.isGooglePlayServicesAvailable;
import static com.google.android.gms.common.GooglePlayServicesUtil.getErrorDialog;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;


public class MapActivity extends GeocodingActivity {
	public static final String SCOPE_NAME = "scope";
	private static final String CAMERA_LOCATION = "/DCIM/Camera";
	private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
	private Geocoder geocoder;
	private GoogleMap googleMap;
	private List<MarkerOptions> markers = new ArrayList<MarkerOptions>();
	private Polyline path;
	private int albumSize = 0;
	private boolean allQueriesSent = false;
	private int picturesLogged = 0;
	private Scope scope = Scope.CITY;
	private PolylineOptions pathBuilder = new PolylineOptions();
	private SortedAlbum cities = new SortedAlbum();
	private SortedSet<PictureWithLocation> sortedPictures = new TreeSet<PictureWithLocation>(PictureWithLocation.pictureComparator);



	public static class PictureWithLocation {
		File fileLoc;
		LatLng latLong;
		boolean hasAddress;
		Address address;
		String city;
		String time;

		public String get(Scope scope) {
			return scope.get(address);
		}

		public static Comparator<PictureWithLocation> pictureComparator = new Comparator<PictureWithLocation>() {
			public int compare(PictureWithLocation arg0, PictureWithLocation arg1) {
				return arg0.time.compareTo(arg1.time);
			}
		};

	}

	public static enum Scope {

		COUNTRY { public String get(Address address) { return address.getCountryName(); }
		public Scope nextLarger() { return null; } },
		STATE   { public String get(Address address) { return address.getAdminArea(); }
		public Scope nextLarger() { return COUNTRY;} },
		CITY    { public String get(Address address) { return address.getLocality(); } 
		public Scope nextLarger() { return STATE;} },
		FEATURE { public String get(Address address) { return address.getFeatureName(); } 
		public Scope nextLarger() { return CITY;} },
		SELF    { public String get(Address address) { return null; } 
		public Scope nextLarger() { return FEATURE;} };
		public abstract String get(Address address);
		public abstract Scope nextLarger();
	}


	public static class NamedLocation {
		public String name;
		public LatLng latLong;
	}
	
	private static class SortedAlbum {
		Map<String, Set<PictureWithLocation>> cityMap = new HashMap<String, Set<PictureWithLocation>>();
		public void addPicture(String city, PictureWithLocation picture) {
			if(cityMap.containsKey(city)) {
				cityMap.get(city).add(picture);
			} else {
				HashSet<PictureWithLocation> set = new HashSet<PictureWithLocation>();
				set.add(picture);
				cityMap.put(city, set);
			}
		}

		public Set<String> getCities() {
			return cityMap.keySet();
		}

	}



	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Intent intent = getIntent();
		String scopeString = intent.getStringExtra(SCOPE_NAME);
		scope = Enum.valueOf(Scope.class, scopeString);
		int resultCode = isGooglePlayServicesAvailable(this);
		if(ConnectionResult.SUCCESS != resultCode) {
			Dialog errorDialog = getErrorDialog(resultCode, this,
					CONNECTION_FAILURE_RESOLUTION_REQUEST);
			if(errorDialog != null) {
				/*// Create a new DialogFragment for the error dialog
				DialogFragment errorFragment =
						new DialogFragment();
				// Set the dialog in the DialogFragment
				errorFragment.setDialog(errorDialog);
				// Show the error dialog in the DialogFragment
				errorFragment.show(
						getSupportFragmentManager(),
						"Activity Recognition");*/
			}
		}

		setContentView(R.layout.activity_main);
		try {
			MapsInitializer.initialize(this);
		} catch (GooglePlayServicesNotAvailableException e) {
			error("Google Play Services are not available at this time.");
			finish();
		}
		geocoder = new Geocoder(this);
		MapFragment mapView = (MapFragment) getFragmentManager().findFragmentById(R.id.map);
		googleMap = mapView.getMap();
	}

	private File getCameraDirectory() {
		return new File(getExternalStorageDirectory() + CAMERA_LOCATION);
	}

	public void onCameraButtonClicked(View view) {
		logGeoLocations();
	}

	public void logGeoLocations() {
		File cameraDir = getCameraDirectory();
		logGeoLocations(cameraDir);
		allQueriesSent = true;
	}


	private void logGeoLocations(File dir) {
		for(File file : dir.listFiles()) {
			try {
				if(file.isDirectory()) logGeoLocations(file);
				if(!file.isFile() || !file.canRead()) continue;
				PictureWithLocation pictureLocation = new PictureWithLocation();
				pictureLocation.fileLoc = file;
				if(!getGeoData(file, pictureLocation)) continue;
				albumSize++;
				new UnGeocoderTask(this).execute(pictureLocation);
			} catch(IOException e) {
				e.printStackTrace();
			}
		}

	}


	private boolean getGeoData(File file, PictureWithLocation pictureLocation) throws IOException {
		ExifInterface exifInterface = new ExifInterface(file.getAbsolutePath());
		float[] latLongArray = new float[2];
		boolean hasGeoData = exifInterface.getLatLong(latLongArray);
		if(!hasGeoData) {
			return false;
		}
		pictureLocation.latLong = new LatLng(latLongArray[0], latLongArray[1]);
		pictureLocation.time = exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
		return true;
	}

	//Separate the lookp from creating the marker. The lookup should create the data structure.
	public void alertGeocodingComplete(Address result, PictureWithLocation picture) {
		if(result == null) result = picture.address;
		LatLng latLong = new LatLng(result.getLatitude(), result.getLongitude());
		pathBuilder.add(latLong);
		//BitmapDescriptor bmp = BitmapDescriptorFactory.fromPath(picture.fileLoc.getAbsolutePath());
		MarkerOptions markerOptions = new MarkerOptions()
		.position(latLong);
		//.icon(bmp);
		String featureName = picture.get(Scope.FEATURE);
		if(featureName != null) {
			markerOptions.snippet(featureName);
		}
		markers.add(markerOptions);
		googleMap.addMarker(markerOptions);
		redraw();
	}


	private void redraw() {
		googleMap.clear();
		for(MarkerOptions marker : markers) {
			googleMap.addMarker(marker);
		}
		path = googleMap.addPolyline(pathBuilder);
	}


	public void alertUnGeocodingComplete(PictureWithLocation result) {
		if(result != null) {
			String pictureCity = result.address.getLocality();
			cities.addPicture(pictureCity, result);
			sortedPictures.add(result);
			picturesLogged++;
			if(allQueriesSent && picturesLogged == albumSize) {
				displayPath(sortedPictures);
			}
		}
	}


	private void displayPath(SortedSet<PictureWithLocation> pictures) {

		for(PictureWithLocation picture : pictures) {
			new GeocoderTask(this, picture, scope).execute((Void)null);
		}
	}
}